﻿namespace Common.ChartDrawing
{
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Shapes;

    using Common.Helpers;
    using Common.Protocol;

    public static  class ChartInitializer
    {
        public static void InitChannelChart(double blockStart, double blockHeight, Channel channel, CanvasElements elements,
            DateTime start, DateTime end)
        {
            double y = blockStart;

            if (ChannelDispatcher.IsActive(channel.Id))
            {
                double top, bottom;

                Polyline line = new Polyline();

                double lastX = -100;

                Color color = MixedConverter.ConvertColor(channel.Color);
                line.Stroke = new SolidColorBrush(color);
                line.StrokeThickness = 2;
                line.Stretch = Stretch.Fill;
                int leftMargin = 1000;

                #region drawing first period

                var firstCompletePeriodStartTime = TimeCounter.FirstCompletePeriodStart(start, channel.Block.Start, channel.RegPeriod);
                double firstPeriodHeight = blockHeight * (firstCompletePeriodStartTime - start).TotalMilliseconds
                                           / (end - start).TotalMilliseconds;
                double startChart, endChart, periodHeight;
                int periods, i;

                if (firstCompletePeriodStartTime != start)
                {
                    if (channel.Values[0] <= 32500)
                    {
                        line.Points.Add(new Point((channel.Values[0] >> 6) + Constants.FieldsWidth, blockStart));
                        line.Points.Add(
                            new Point((channel.Values[0] >> 6) + Constants.FieldsWidth, blockStart - firstPeriodHeight));
                        leftMargin = (channel.Values[0] >> 6) + Constants.FieldsWidth;
                    }

                    startChart = blockStart - firstPeriodHeight;
                    periods = channel.Values.Count - 2;
                    i = 1;
                }
                else
                {
                    startChart = blockStart;
                    periods = channel.Values.Count - 1;
                    i = 0;
                }

                var lastPeriodStartTime = TimeCounter.LastCompletePeriodStart(
                    end, channel.Block.Start, channel.RegPeriod);
                double lastPeriodStart = (blockStart - blockHeight)
                    + blockHeight * (end - lastPeriodStartTime).TotalMilliseconds / (end - start).TotalMilliseconds;
                endChart = lastPeriodStart;
                periodHeight = (startChart - endChart) / periods;

                #endregion

                #region drawing complete middle periods

                if (startChart != endChart)
                {
                    y = startChart;

                    for (; i <= periods; i++)
                    {
                        int value = channel.Values[i];
                        if (lastX != -100)
                        {
                            line.Points.Add(new Point(lastX, y));
                        }

                        if (value <= 32500)
                        {
                            lastX = (value >> 6) + Constants.FieldsWidth;
                            if ((int)lastX < leftMargin)
                            {
                                leftMargin = (int)lastX;
                            }

                            line.Points.Add(new Point(lastX, y));
                        }
                        else
                        {
                            if (line.Points.Count != 0)
                            {
                                line.Points.Add(new Point(line.Points[line.Points.Count - 1].X, y));
                                top = line.Points[line.Points.Count - 1].Y;
                                bottom = line.Points[0].Y;
                                line.Height = bottom - top;
                                line.Margin = new Thickness(
                                    leftMargin - Constants.FieldsWidth, top, line.Margin.Right, bottom);
                                Canvas.SetZIndex(line, 2);
                                elements.Charts.Add(line);
                            }

                            line = new Polyline();
                            lastX = -100;
                            line.Stroke = new SolidColorBrush(color);
                            line.StrokeThickness = 2;
                            line.Stretch = Stretch.Fill;
                            leftMargin = 1000;
                        }

                        y -= periodHeight;
                        if (y < blockStart - blockHeight)
                        {
                            y = blockStart - blockHeight;
                        }
                    }

                    if (line.Points.Count != 0)
                    {
                        line.Points.Add(new Point(line.Points[line.Points.Count - 1].X, y));
                    }
                }

                #endregion

                #region drawing last period

                if (line.Points.Count != 0 && channel.Values[channel.Values.Count - 1] <= 32500
                    && startChart != blockStart)
                {
                    line.Points.Add(new Point((channel.Values[channel.Values.Count - 1] >> 6) 
                        + Constants.FieldsWidth, lastPeriodStart));
                    line.Points.Add(new Point((channel.Values[channel.Values.Count - 1] >> 6) 
                        + Constants.FieldsWidth, blockStart - blockHeight));
                    if ((channel.Values[channel.Values.Count - 1] >> 6) + Constants.FieldsWidth < leftMargin)
                    {
                        leftMargin = (channel.Values[channel.Values.Count - 1] >> 6) + Constants.FieldsWidth;
                    }
                }
                
                #endregion

                if (line.Points.Count != 0)
                {
                    top = line.Points[line.Points.Count - 1].Y;
                    bottom = line.Points[0].Y;
                    if (double.IsInfinity(top))
                    {
                        top = 0;
                    }

                    line.Height = bottom - top;
                    line.Margin = new Thickness(leftMargin - Constants.FieldsWidth, top, line.Margin.Right, bottom);
                    Canvas.SetZIndex(line, 2);
                    elements.Charts.Add(line);
                }
            }
        }


        public static void InitCompressedChannelChart(double blockStart, double blockHeight,
            Channel channel, CanvasElements elements, DateTime start, DateTime end)
        {
            if (ChannelDispatcher.IsActive(channel.Id))
            {
                double top, bottom, lastX = -100, blockEnd = blockStart - blockHeight;
                int leftMargin = 1000;

                Polyline line = new Polyline();
                Color color = MixedConverter.ConvertColor(channel.Color);
                line.Stroke = new SolidColorBrush(color);
                line.StrokeThickness = 2;
                line.Stretch = Stretch.Fill;
                Canvas.SetZIndex(line, 2);
                double y = blockStart;
                DateTime currentTime = start;
                DateTime nextTime;

                for (; y >= blockEnd; y--)
                {
                    nextTime = currentTime.AddMilliseconds(Constants.MillisecondsByPixel);
                    if (nextTime > end)
                    {
                        nextTime = end;
                    }

                    int min = channel.GetMin(currentTime, nextTime, start);
                    int max = channel.GetMax(currentTime, nextTime, start);

                    if (min == max && min <= 32500)
                    {
                        if ((min >> 6) + Constants.FieldsWidth < leftMargin)
                        {
                            leftMargin = (min >> 6) + Constants.FieldsWidth;
                        }

                        line.Points.Add(new Point((min >> 6) + Constants.FieldsWidth, y));
                    }
                    else
                    {
                        if (max <= 32500 && min <= 32500)
                        {
                            if ((min >> 6) + Constants.FieldsWidth < leftMargin)
                            {
                                leftMargin = (min >> 6) + Constants.FieldsWidth;
                            }

                            line.Points.Add(new Point((min >> 6) + Constants.FieldsWidth, y));
                            line.Points.Add(new Point((max >> 6) + Constants.FieldsWidth, y));
                        }
                        else
                        {
                            if (min <= 32500 && max > 32500)
                            {
                                if ((min >> 6) + Constants.FieldsWidth < leftMargin)
                                {
                                    leftMargin = (min >> 6) + Constants.FieldsWidth;
                                }

                                line.Points.Add(new Point((min >> 6) + Constants.FieldsWidth, y));    
                            }
                        }

                        if (min > 32500 && max > 32500 && line.Points.Count != 0)
                        {
                            top = line.Points[line.Points.Count - 1].Y;
                            bottom = line.Points[0].Y;
                            line.Height = bottom - top;
                            line.Margin = new Thickness(leftMargin - Constants.FieldsWidth, top, line.Margin.Right, bottom);
                            elements.Charts.Add(line);
                            leftMargin = 1000;
                            line = new Polyline();
                            line.Stroke = new SolidColorBrush(color);
                            line.StrokeThickness = 2;
                            line.Stretch = Stretch.Fill;
                            Canvas.SetZIndex(line, 2);
                        }
                    }

                    currentTime = nextTime;
                }

                if (line.Points.Count != 0)
                {
                    line.Points.Add(new Point(line.Points[line.Points.Count - 1].X, y));
                    top = line.Points[line.Points.Count - 1].Y;
                    bottom = line.Points[0].Y;
                    line.Height = bottom - top;
                    line.Margin = new Thickness(leftMargin - Constants.FieldsWidth, top, line.Margin.Right, bottom);
                    Canvas.SetZIndex(line, 2);
                    elements.Charts.Add(line);
                }
            }
        }
    }
}